package com.zbs.simbot.plugins.genshin.service.impl;

import cn.hutool.extra.qrcode.QrCodeUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.zbs.simbot.constants.MhyConstant;
import com.zbs.simbot.data.MysUserDataUtil;
import com.zbs.simbot.data.mysuser.MysActivateCookieGameRole;
import com.zbs.simbot.data.mysuser.MysCookie;
import com.zbs.simbot.data.mysuser.MysGameRole;
import com.zbs.simbot.data.mysuser.MysUid;
import com.zbs.simbot.events.BaseMsgEvent;
import com.zbs.simbot.plugins.genshin.service.MysUserService;
import com.zbs.simbot.utils.HttpUtils;
import com.zbs.simbot.utils.LambadaTools;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;

@Service
public class MysUserServiceImpl implements MysUserService {

    /**
     * 绑定UID
     *
     * @param event QQ消息事件
     */
    @Override
    public void bingUid(BaseMsgEvent event) {
        String msg = event.getMsg();
        Long userId = event.getUserId();
        String uid = MhyConstant.getUidToMsg(msg);

        Map<String, MysCookie> cookieList = MysUserDataUtil.getCookieList(userId);
        if (CollectionUtils.isEmpty(cookieList)) {
            MysUserDataUtil.saveUid(uid, userId);
            event.send("绑定成功uid:" + uid);
        } else {
            Map<String, Map<String, MysGameRole>> gameRoles = MysUserDataUtil.getGameRoles(userId);
            gameRoles.forEach((tuid, v) -> {
                v.forEach((guid, role) -> {
                    if (guid.equals(uid)) {
                        MysCookie mysCookie = cookieList.get(tuid);
                        MysUserDataUtil.saveActivateCookieGameRole(userId, mysCookie, role);
                        MysUserDataUtil.saveUid(uid, userId);
                        event.send("绑定成功uid:" + uid);
                    }
                });
            });
        }
    }

    /**
     * 查看UID
     * @param event QQ消息事件
     */
    @Override
    public void showUid(BaseMsgEvent event) {
        // 保存角色
        Map<String, Map<String, MysGameRole>> saveGameRoles = MysUserDataUtil.getGameRoles(event.getUserId());
        if (CollectionUtils.isEmpty(saveGameRoles)) {
            MysUid uid = MysUserDataUtil.getUid(event.getUserId());
            if (uid == null) {
                event.send("当前暂无绑定的uid");
                return;
            }
            event.send("当前绑定uid：" + uid.getUid());
            return;
        }
        // 保存当前使用uid
        MysActivateCookieGameRole activate = MysUserDataUtil.getActivateCookieGameRole(event.getUserId());

        BaseMsgEvent.MsgBuilder builder = event.builder();
        builder.text("当前uid：" + activate.getGameRole().getGame_uid() + "\n");
        builder.text("当前绑定cookie Uid列表 \n");
        saveGameRoles
                .values()
                .stream()
                .flatMap(m -> m.values().stream())
                .forEach(role -> {
                    String tag = "";
                    if (activate.getGameRole().getGame_uid().equals(role.getGame_uid())) {
                        tag = "☑";
                    }
                    builder.text(String.format("服务名：%s，uid：%s，游戏名：%s，等级：%s %s\n", role.getRegion_name(), role.getGame_uid(), role.getNickname(), role.getLevel(), tag));
                });
        builder.text("通过【#绑定uid+uid】来切换uid \n");
        builder.sendBlocking();
    }

    /**
     * 米游社登录
     * @param event QQ消息事件
     */
    @Override
    public void mhyLoginQrCode(BaseMsgEvent event) {
        if (running(event.getUserId())) {
            event.reply("有正在进行的登录操作，请完成后再试……");
            return;
        }

        this.running(event.getUserId(), true);


        String device = RandomStringUtils.random(64, MhyConstant.SEED);
        // 请求参数
        Map<String, Object> params = new HashMap<>();
        params.put("app_id", MhyConstant.APP_ID);
        params.put("device", device);
        // 请求 获取米游社登录二维码url
        String resp = HttpUtils.postToJson(MhyConstant.MHY_GET_QR_CODE_URL, params);
        // 获取米游社登录二维码url
        String url = JSONObject.parseObject(resp).getJSONObject("data").getString("url");
        String ticket = url.split("ticket=")[1];

        byte[] bytes = QrCodeUtil.generatePng(url, 350, 350);

        event.builder()
                .text("请使用米游社扫码登录")
                .image(bytes, "米游社登录二维码")
                .replyBlocking();

        CompletableFuture.runAsync(() -> queryQrCodeIsLogin(event, device, ticket));

    }

    /**
     * 查询是否扫码登录
     * @param event QQ消息事件
     * @param device 设备码
     * @param ticket 米游社 ticket
     */
    private void queryQrCodeIsLogin(BaseMsgEvent event, String device, String ticket) {
        JSONObject data = null;
        String message = "";
        // 每5秒一次轮询，轮询60次
        for (int i = 1; i < 60; i++) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException ignored) {
            }
            // 请求参数
            Map<String, Object> params = new HashMap<>();
            params.put("app_id", MhyConstant.APP_ID);
            params.put("device", device);
            params.put("ticket", ticket);
            // 请求 检查二维码是否已被使用
            String resp = HttpUtils.postToJson(MhyConstant.MHY_QUERY_QR_CODE_URL, params);
            JSONObject jsonObject = JSONObject.parseObject(resp);
            Integer retcode = jsonObject.getInteger("retcode");
            if (retcode != 0) {
                message = "二维码已过期，请重新获取二维码";
                break;
            }

            String stat = jsonObject.getJSONObject("data").getString("stat");
            if (stat.equals("Scanned")) {
                event.send("二维码已扫描，请确认登录");
            }
            // 扫码完成
            if (stat.equals("Confirmed")) {
                data = JSONObject.parseObject(resp).getJSONObject("data").getJSONObject("payload").getJSONObject("raw");
                break;
            }
        }

        //  登录异常判断
        if (data == null || !StringUtils.hasLength(data.getString("uid")) || !StringUtils.hasLength(data.getString("token"))) {
            event.send(StringUtils.hasLength(message) ? message : MhyConstant.ERROR_TIPS);
            this.running(event.getUserId(), false);
            return;
        }

        Long uid = data.getLong("uid");
        String gameToken = data.getString("token");
        // 请求参数
        Map<String, Object> params = new HashMap<>();
        params.put("account_id", uid);
        params.put("game_token", gameToken);

        // 请求 获取token
        String resp = HttpUtils.postToJson(MhyConstant.GET_TOKEN_BY_GAME_TOKEN_URL, params, getGameTokenParams(params, ""));
        JSONObject gameTokenRespJson = JSONObject.parseObject(resp);
        if (gameTokenRespJson.getInteger("retcode") != 0) {
            event.send(MhyConstant.ERROR_TIPS);
            this.running(event.getUserId(), false);
            return;
        }

        // 请求参数 获取cookie
        String cookieResp = HttpUtils.get(MhyConstant.GET_COOKIE_ACCOUNT_INFO_BY_GAME_TOKEN_URL, params);
        JSONObject cookieRespJson = JSONObject.parseObject(cookieResp);

        if (cookieRespJson.getInteger("retcode") != 0) {
            event.send(MhyConstant.ERROR_TIPS);
            this.running(event.getUserId(), false);
            return;
        }

        MysCookie mysCookie = new MysCookie(event.getUserId(), gameTokenRespJson, cookieRespJson);

        String cookies = mysCookie.getCookies();
        String stoken = mysCookie.getSToken();
        event.send(cookies);
        event.send(stoken);
        event.send("登录完成，以上分别是 Cookie 和 Stoken，发送给 Bot 完成绑定");

        getUserGameRolesByCookie(event, mysCookie);
    }

    private void getUserGameRolesByCookie(BaseMsgEvent event, MysCookie mysCookie) {
        // 请求参数
        Map<String, String> headers = new HashMap<>();
        headers.put("Cookie", mysCookie.getCookies());

        JSONObject roleRes = null;
        String message = "error";
        for (String serv : MhyConstant.SERVS) {
            String url = MhyConstant.USER_GAME_ROLES_URL.get(serv);
            // 请求 获取角色列表
            String resp = HttpUtils.get(url, new HashMap<>(), headers);
            JSONObject respJson = JSON.parseObject(resp);
            if (respJson.getInteger("retcode") == 0) {
                roleRes = respJson;
                break;
            } else {
                message = respJson.getString("message");
            }
        }
        if (roleRes != null) {
            String string = roleRes.getJSONObject("data").getString("list");
            List<MysGameRole> mysGameRoles = JSON.parseArray(string, MysGameRole.class);
            if (CollectionUtils.isEmpty(mysGameRoles)) {
                event.send("该账号尚未绑定原神角色");
                this.running(event.getUserId(), false);
                return;
            }
            BaseMsgEvent.MsgBuilder builder = event.builder();
            builder.text("绑定cookie成功,当前已绑定以下uid\n");

            // 保存cookie
            MysUserDataUtil.saveCookie(event.getUserId(), mysCookie);
            // 保存角色
            Map<String, Map<String, MysGameRole>> saveGameRoles = MysUserDataUtil.saveGameRoles(event.getUserId(), mysCookie, mysGameRoles);
            // 保存当前使用uid
            MysActivateCookieGameRole activate = MysUserDataUtil.saveActivateCookieGameRole(event.getUserId(), mysCookie, mysGameRoles.get(0));

            saveGameRoles
                    .values()
                    .stream()
                    .flatMap(m -> m.values().stream())
                    .forEach(LambadaTools.forEachWithIndex((role, i) -> {
                        String tag = "";
                        if (activate.getGameRole().getGame_uid().equals(role.getGame_uid())) {
                            tag = "☑";
                        }
                        builder.text(String.format("%s. 服务名：%s，uid：%s，游戏名：%s，等级：%s %s\n", i, role.getRegion_name(), role.getGame_uid(), role.getNickname(), role.getLevel(), tag));
                    }));
            builder.text("通过【#绑定uid+uid】来切换uid \n");
            builder.sendBlocking();


        } else {
            event.send("绑定cookie失败:" + message);
        }

        this.running(event.getUserId(), false);
    }

    private Map<String, String> getGameTokenParams(Map<String, Object> params, String aigis) {
        Map<String, String> headers = new HashMap<>();
        headers.put("x-rpc-app_version", "2.41.0");
        headers.put("DS", ds(JSONObject.toJSONString(params)));
        headers.put("x-rpc-aigis", aigis);
        headers.put("x-rpc-game_biz", "bbs_cn");
        headers.put("x-rpc-sys_version", "12");
        headers.put("x-rpc-device_id", RandomStringUtils.random(16, MhyConstant.SEED));
        headers.put("x-rpc-device_fp", RandomStringUtils.random(13, MhyConstant.SEED));
        headers.put("x-rpc-device_name", RandomStringUtils.random(16, MhyConstant.SEED));
        headers.put("x-rpc-device_model", RandomStringUtils.random(16, MhyConstant.SEED));
        headers.put("x-rpc-app_id", "bll8iq97cem8");
        headers.put("x-rpc-client_type", "2");
        headers.put("User-Agent", "okhttp/4.8.0");
        return headers;
    }

    private String ds(String data) {
        String t = new BigDecimal(System.currentTimeMillis()).divide(new BigDecimal(1000), 0, RoundingMode.HALF_UP).toString();
        String r = RandomStringUtils.random(6, MhyConstant.SEED);
        String h = DigestUtils.md5DigestAsHex(String.format("salt=JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS&t=%s&r=%s&b=%s&q=", t, r, data).getBytes());
        return String.format("%s,%s,%s", t, r, h);
    }

    private static String getServKey(String uid) {
        // java 正在和js正在好像有点不一样
        // /^[6-9]|^hoyo|^os/i
        return Pattern.matches("^([6-9]|^(?i)hoyo|^(?i)os).*", uid) ? MhyConstant.SERVS[1] : MhyConstant.SERVS[0];
    }

    private boolean running(Long id) {
        return MhyConstant.qrLoginRunning.containsKey(id.toString()) && MhyConstant.qrLoginRunning.get(id.toString());
    }

    private void running(Long id, Boolean isRunning) {
        MhyConstant.qrLoginRunning.put(id.toString(), isRunning);
    }


}
